package ptij.ADSL.de.ATUR;

import ptolemy.kernel.*;
import ptolemy.kernel.util.*;
import ptolemy.data.*;
import ptolemy.data.type.*;
import ptolemy.data.expr.*;
import ptolemy.actor.*;

import ptij.ADSL.common.MuxDataFrame;

//////////////////////////////////////////////////////////////////////////
//// CRCRx
/**

@author Ivan Jeukens
*/
public class CRCRx extends TypedAtomicActor {
    
    public CRCRx(CompositeEntity container, String name) throws
            NameDuplicationException, IllegalActionException {
        super(container, name);
	
	input = new TypedIOPort(this, "input", true, false);
	input.setTypeEquals(BaseType.OBJECT);
	
	output = new TypedIOPort(this, "output", false, true);
	output.setTypeEquals(BaseType.OBJECT);

	AS0Bytes = new Parameter(this, "AS0Bytes", new IntToken(8));
	LS0Bytes = new Parameter(this, "LS0Bytes", new IntToken(8));
		
	AS0Map = new Parameter(this, "AS0Map", new BooleanToken(false));
	LS0Map = new Parameter(this, "LS0Map", new BooleanToken(false));	
	
	S = new Parameter(this, "S", new IntToken(1));
    }

    ///////////////////////////////////////////////////////////////////
    ////                     ports and parameters                  ////
    
    public TypedIOPort input;   
    public TypedIOPort output;

    public Parameter AS0Bytes;
    public Parameter LS0Bytes;
    public Parameter AS0Map;
    public Parameter LS0Map;    
    public Parameter S;

    ///////////////////////////////////////////////////////////////////
    ////                         public methods                    ////

    public void attributeChanged(Attribute attribute)
            throws IllegalActionException {
        if(attribute == AS0Bytes) {
            IntToken it = (IntToken) AS0Bytes.getToken();
	    _AS0Bytes = it.intValue();    
        }
        else
        if(attribute == LS0Bytes) {
            IntToken it = (IntToken) LS0Bytes.getToken();
            _LS0Bytes = it.intValue();            
        }
        else
        if(attribute == AS0Map) {
	    BooleanToken _bt = (BooleanToken) AS0Map.getToken();
	    _AS0Map = _bt.booleanValue();            
        }
        else
        if(attribute == LS0Map) {
            BooleanToken bt = (BooleanToken) LS0Map.getToken();
	    _LS0Map = bt.booleanValue();   
        }
        else
        if(attribute == S) {
 	    IntToken it = (IntToken) S.getToken();
	    _S = it.intValue();           
        }
	else {
            super.attributeChanged(attribute);
        }                
    }

    public void initialize() throws IllegalActionException {
        super.initialize();
	
        _checkParameters();
		
	_iteration = 0;
        _frameCounter = 0;
	_interReg = 0;
	_fastReg = 0;
    }
    
    public void fire() throws IllegalActionException {    
        ObjectToken ot = (ObjectToken) input.get(0);
	MuxDataFrame mf = (MuxDataFrame) ot.getValue();
	
	if(_frameCounter == 0) {
            if(_iteration == 0) {
	        _fastCRC(mf, false);
	        _iteration = 1;
	    }
	    else {
	        _CRC(0, _fastReg);	        
		int check = mf.getFastByte();
		if((byte) check != (byte) _fastReg) {
//		    System.out.println(" \t\t\t\t !!!!! ATUR-CRCRx : FAST CRC FAILED !!!!! ");
		}
		else {
//		    System.out.println(" \t\t\t\t #### ATUR-CRCRx : FAST CRC OK ####");
		}                
	        _fastReg = 0;
	        _fastCRC(mf, true);
	    }
	}		
	else
	if(_frameCounter == 1) {	 
            if(_iteration == 1) {
	        _interCRC(mf, false);
	        _iteration = 2;
	    }
	    else {
	        _CRC(0, _interReg);	        
		int check = mf.getFastByte();
		if((byte) check != (byte) _interReg) {
//		    System.out.println(" \t\t\t\t !!!!! ATUR-CRCRx : INTER CRC FAILED !!!!!");
		}
		else {
//		    System.out.println(" \t\t\t\t #### ATUR-CRCRx : INTER CRC OK ####");
		}
	        _interReg = 0;
	        _interCRC(mf, true);
	    }
	}
	else {
	    if(_frameCounter % (1 + _S) == 0) {
	        _fastCRC(mf, true);
	    }
	    else {
	        _interCRC(mf, true);
	    }
	}

	_frameCounter++;
	if(_frameCounter == 
	       ATUR.framesPerSuperframe + ATUR.framesPerSuperframe*_S) 
	    _frameCounter = 0;
	
	ot = new ObjectToken(mf);
	output.broadcast(ot);
    }

    ///////////////////////////////////////////////////////////////////
    ////                         private methods                   ////
       
    private void _fastCRC(MuxDataFrame mf, boolean first) {
        if(first) {
	    _fastReg = _CRC(mf.getFastByte(), _fastReg);
	}
	
	if(!_AS0Map) {
	    for(int i = 0;i < _AS0Bytes;i++)
	        _fastReg = _CRC(mf.getAS0Byte(i), _fastReg);
	}
	
	if(!_LS0Map) {
	    for(int i = 0;i < _LS0Bytes;i++)
	        _fastReg = _CRC(mf.getLS0Byte(i), _fastReg);
	}
	
	if(mf.hasAEX()) {
	    _fastReg = _CRC(mf.getAEXByte(), _fastReg);
	}
	
	if(mf.hasLEX()) {
	    _fastReg = _CRC(mf.getLEXByte(), _fastReg);
	}
    }
    
    private void _interCRC(MuxDataFrame mf, boolean first) {
        if(first) {
	    _interReg = _CRC(mf.getFastByte(), _interReg);	
	}
	
	if(_AS0Map) {
	    for(int i = 0;i < _AS0Bytes;i++)
	        _interReg = _CRC(mf.getAS0Byte(i), _interReg);
	}
	
	if(_LS0Map) {
	    for(int i = 0;i < _LS0Bytes;i++)
	        _interReg = _CRC(mf.getLS0Byte(i), _interReg);
	}
	
	if(mf.hasAEX()) {
	    _interReg = _CRC(mf.getAEXByte(), _interReg);
	}
	
	if(mf.hasLEX()) {
	    _interReg = _CRC(mf.getLEXByte(), _interReg);
	}
    }
    
    private int _CRC(int b, int reg) {
        int out;
        for(int i = 0;i < 8;i++) {
	    out = reg & 0x80;
	    reg = (reg << 1) | ((b >> i) & 0x01);
	    if(out != 0) reg = reg ^ _poly;
	}	
	return reg;
    }

    private void _checkParameters() throws IllegalActionException {
    
        if(_AS0Bytes < 0) {
	    throw new IllegalActionException(this, "AS0 Bytes < 0");	
	}
	
	if(_LS0Bytes < 0) {
	    throw new IllegalActionException(this, "LS0 Bytes < 0");
	}
	
	if(_S != 1 && _S != 2 && _S != 4 && _S != 8 && _S != 16) {
	    throw new IllegalActionException(this, "\nInvalid value(" +
	        _S + ") for parameter S");
	}	
    }
 
    ///////////////////////////////////////////////////////////////////
    ////                         private variables                 ////            
       
    private int _frameCounter;
    private int _fastReg;
    private int _interReg;
    private final static int _poly = 0x1d;
    private byte _iteration;

    private int _AS0Bytes;
    private int _LS0Bytes;
    private boolean _AS0Map;
    private boolean _LS0Map;
    private int _S;
}
